#include "const/oPreDefine.h"
#include "model/oModelDef.h"
#include "const/oPreDefine.h"
#include "model/oActionInterval.h"
#include "model/oAnimationCache.h"
#include "misc/oContent.h"
#include "const/oXml.h"
#include "model/oEase.h"

oClipDef::oClipDef()
{ }

oClipDef* oClipDef::create()
{
	oClipDef* clipDef = new oClipDef();
	clipDef->autorelease();
	return clipDef;
}

CCSprite* oClipDef::toSprite( uint32 index )
{
	if (index < rects.size())
	{
		CCTexture2D* texture = oSharedContent.loadTexture(textureFile.c_str());
		CCSprite* sprite = CCSprite::spriteWithTexture(texture, rects[index]);
		return sprite;
	}
	return nullptr;
}

string oClipDef::toXml()
{
	ostringstream stream;
	stream << '<' << char(oClipXml::Texture) << ' ' << char(oClipXml::File) << "=\""
		<< textureFile << "\">";
	BOOST_FOREACH(const CCRect& rect, rects)
	{
		stream << '<' << char(oClipXml::Clip) << ' '
			<< char(oClipXml::Rect) << "=\""
			<< rect.origin.x << ',' << rect.origin.y << ','
			<< rect.size.width << ',' << rect.size.height
			<< "\"/>";
	}
	stream << "</" << char(oClipXml::Texture) << '>';
	return stream.str();
}

oKeyFrameDef::oKeyFrameDef(float duration, float x, float y, float rotation, float scaleX, float scaleY, GLubyte opacity):
_duration(duration),
_x(x),
_y(y),
_rotation(rotation),
_scaleX(scaleX),
_scaleY(scaleY),
_opacity(opacity),
easePos(0),
easeRotate(0),
easeScale(0),
easeOpacity(0)
{ }

oAnimateTo* oKeyFrameDef::toAction()
{
	oAnimateTo* animateTo = oAnimateTo::actionWithDuration(_duration, _x, _y, _scaleX, _scaleY, _rotation, _opacity);
	if (easePos) animateTo->setEase(oAnimateTo::Pos, oEase::get(easePos));
	if (easeScale) animateTo->setEase(oAnimateTo::Scale, oEase::get(easeScale));
	if (easeRotate) animateTo->setEase(oAnimateTo::Rotate, oEase::get(easeRotate));
	if (easeOpacity) animateTo->setEase(oAnimateTo::Opacity, oEase::get(easeOpacity));
	return animateTo;
}

string oKeyFrameDef::toXml()
{
	ostringstream stream;
	stream << '<' << char(oModelXml::Frame);
	if (_duration != 0.0f)
	{
		stream << ' ' << char(oModelXml::Duration) << "=\"" << _duration << '\"';
	}
	if (_x != 0.0f && _y != 0.0f)
	{
		stream << ' ' << char(oModelXml::Position) << "=\"" << _x << ',' << _y << '\"';
	}
	if (_rotation != 0.0f)
	{
		stream << ' ' << char(oModelXml::Rotation) << "=\"" << _rotation << '\"';
	}
	if (_scaleX != 1.0f || _scaleY != 1.0f)
	{
		stream << ' ' << char(oModelXml::Scale) << "=\"" << _scaleX << ',' << _scaleY << '\"';
	}
	if (_opacity != 255)
	{
		stream << ' ' << char(oModelXml::Opacity) << "=\"" << (uint32)_opacity << '\"';
	}
	if (easePos != 0)
	{
		stream << ' ' << char(oModelXml::EasePos) << "=\"" << easePos << '\"';
	}
	if (easeScale != 0)
	{
		stream << ' ' << char(oModelXml::EaseScale) << "=\"" << easeScale << '\"';
	}
	if (easeRotate != 0)
	{
		stream << ' ' << char(oModelXml::EaseRotate) << "=\"" << easeRotate << '\"';
	}
	if (easeOpacity != 0)
	{
		stream << ' ' << char(oModelXml::EaseOpacity) << "=\"" << easeOpacity << '\"';
	}
	stream << "/>";
	return stream.str();
}

void oAnimationDef::add( oKeyFrameDef* def )
{
	_frames.push_back(def);
}

oSequence* oAnimationDef::toAction()
{
	CCArray* array = CCArray::arrayWithCapacity(_frames.size());
	BOOST_FOREACH(oKeyFrameDef& frameDef, _frames)
	{
		array->addObject(frameDef.toAction());
	}
	return oSequence::actionsWithArray(array);
}

string oAnimationDef::toXml()
{
	ostringstream stream;
	stream << '<' << char(oModelXml::Action);
	if (_frames.empty())
	{
		stream << "/>";
	}
	else
	{
		stream << '>';
		BOOST_FOREACH(oKeyFrameDef& frameDef, _frames)
		{
			stream << frameDef.toXml();
		}
		stream << "</" << char(oModelXml::Action) << '>';
	}
	return stream.str();
}

oSequence* oFrameAnimationDef::toAction()
{
	return oSequence::actions(
		CCDelayTime::actionWithDuration(_begin),
		_def->toAction(),
		CCDelayTime::actionWithDuration(_end),
		nullptr);
}

oFrameAnimationDef::oFrameAnimationDef(const char* frameFile, float begin, float end):
_begin(begin),
_end(end),
_frameFile(frameFile),
_def(oSharedAnimationCache.load(frameFile))
{
	_def->retain();
}

oFrameAnimationDef::~oFrameAnimationDef()
{
	CC_SAFE_RELEASE_NULL(_def);
}

string oFrameAnimationDef::toXml()
{
	ostringstream stream;
	stream << '<' << char(oModelXml::Animate) << ' '
		<< char(oModelXml::File) << "=\"" << _frameFile << '\"';
	if (_begin != 0.0f)
	{
		stream << ' ' << char(oModelXml::Begin) << "=\"" << _begin << '\"';
	}
	if (_end != 0.0f)
	{
		stream << ' ' << char(oModelXml::End) << "=\"" << _end << '\"';
	}
	stream << "/>";
	return stream.str();
}

oSpriteDef::~oSpriteDef()
{
	BOOST_FOREACH(oSpriteDef* child, _children)
	{
		delete child;
	}
	_children.clear();
	BOOST_FOREACH(oActionDef* actionDef, _actions)
	{
		delete actionDef;
	}
	_actions.clear();
}

void oSpriteDef::addChild( oSpriteDef* def )
{
	_children.push_back(def);
}

void oSpriteDef::addAction( oActionDef* def )
{
	_actions.push_back(def);
}

void oSpriteDef::addLook( uint32 look )
{
	_looks.push_back(look);
}

int oSpriteDef::getLook( uint32 index )
{
	return _looks[index];
}

uint32 oSpriteDef::lookSize()
{
	return _looks.size();
}

const vector<oSpriteDef*>& oSpriteDef::children()
{
	return _children;
}

oActionDef* oSpriteDef::getAction( uint32 index )
{
	return _actions[index];
}

uint32 oSpriteDef::actionSize()
{
	return _actions.size();
}

CCSprite* oSpriteDef::toSprite()
{
	CCSprite* sprite = CCSprite::spriteWithTexture(_texture, _rect);
	sprite->setAnchorPoint(ccp(_archorX, _archorY));
	sprite->setPosition(ccp(_x, _y));
	sprite->setScaleX(_scaleX);
	sprite->setScaleX(_scaleY);
	sprite->setRotation(_rotation);
	return sprite;
}

void oSpriteDef::restore( CCSprite* sprite )
{	
	sprite->setPosition(ccp(_x, _y));
	sprite->setScaleX(_scaleX);
	sprite->setScaleX(_scaleY);
	sprite->setRotation(_rotation);
	sprite->setOpacity(_opacity);
}

oSpriteDef::oSpriteDef(
	CCTexture2D* tex,
	float x, float y,
	float scaleX, float scaleY,
	float rotation,
	float archorX, float archorY,
	const CCRect& rect,
	int clip, GLubyte opacity ):
_x(x),
_y(y),
_rotation(rotation),
_archorX(archorX),
_archorY(archorY),
_scaleX(scaleX),
_scaleY(scaleY),
_texture(tex),
_rect(rect),
_clip(clip),
_opacity(opacity)
{ }

string oSpriteDef::toXml()
{
	ostringstream stream;
	stream << '<' << char(oModelXml::Sprite);
	if (_x != 0.0f || _y != 0.0f)
	{
		stream << ' ' << char(oModelXml::Position) << "=\"" << _x << ',' << _y << '\"';
	}
	if (_rotation != 0.0f)
	{
		stream << ' ' << char(oModelXml::Rotation) << "=\"" << _rotation << '\"';
	}
	if (_archorX != 0.5f || _archorY != 0.5f)
	{
		stream << ' ' << char(oModelXml::Key) << "=\"" << _archorX << ',' << _archorY << '\"';
	}
	if (_scaleX != 1.0f || _scaleY != 1.0f)
	{
		stream << ' ' << char(oModelXml::Scale) << "=\"" << _scaleX << ',' << _scaleY << '\"';
	}
	if (_clip != -1)
	{
		stream << ' ' << char(oModelXml::Clip) << "=\"" << _clip << '\"';
	}
	stream << '>';
	BOOST_FOREACH(oActionDef* actionDef, _actions)
	{
		if (actionDef)
		{
			stream << actionDef->toXml();
		}
		else
		{
			stream << '<' << char(oModelXml::Action) << "/>";
		}
	}
	if (!_looks.empty())
	{
		stream << '<' << char(oModelXml::Look) << ' '
			<< char(oModelXml::Index) << "=\"";
		int last = _looks.size() - 1;
		for (int i = 0;i < (int)_looks.size();i++)
		{
			stream << _looks[i];
			if (i != last)
			{
				stream << ',';
			}
		}
		stream << "\"/>";
	}
	BOOST_FOREACH(oSpriteDef* spriteDef, _children)
	{
		stream << spriteDef->toXml();
	}
	stream << "</" << char(oModelXml::Sprite) << '>';
	return stream.str();
}

oAnimateTo* oSpriteDef::toResetAction()
{
	return oAnimateTo::actionWithDuration(0, _x, _y, _scaleX, _scaleY, _rotation, _opacity);
}

oModelDef::oModelDef():
_texture(nullptr),
_root(nullptr),
_isFaceRight(false)
{ }

oModelDef::~oModelDef()
{
	CC_SAFE_RELEASE_NULL(_texture);
	CC_SAFE_DELETE(_root);
}

void oModelDef::setTexture( CCTexture2D* tex )
{
	if (_texture != tex)
	{
		CC_SAFE_RELEASE(_texture);
		_texture = tex;
		CC_SAFE_RETAIN(_texture);
	}
}

CCTexture2D* oModelDef::getTexture()
{
	return _texture;
}

void oModelDef::setRoot( oSpriteDef* root )
{
	if (_root != root)
	{
		CC_SAFE_DELETE(_root);
		_root = root;
	}
}

oSpriteDef* oModelDef::getRoot()
{
	return _root;
}

void oModelDef::visit( oSpriteDef* parentDef, CCNode* parentNode )
{
	if (!parentDef)
	{
		return;
	}
	const vector<oSpriteDef*>& children = parentDef->children();
	for (uint32 n = 0;n < children.size();n++)
	{
		oSpriteDef* nodeDef = children[n];
		CCSprite* node = nodeDef->toSprite();

		_pModel->_resetAnimation.add(node, nodeDef->toResetAction());

		node->useBatchNode(_pModel);
		parentNode->addChild(node);
		// Look
		if (nodeDef->lookSize() > 0)
		{
			for (uint32 n = 0;n < nodeDef->lookSize();n++)
			{
				uint32 lookIndex = nodeDef->getLook(n);
				_pModel->addLook(lookIndex, node);
			}
		}
		// Action
		for (uint32 n = 0;n < nodeDef->actionSize();n++)
		{
			oActionDef* actionDef = nodeDef->getAction(n);
			if (actionDef)
			{
				_pModel->addAnimation(n, node, (actionDef->toAction()));
			}
		}
		oModelDef::visit(nodeDef, node);
	}
}

oModel* oModelDef::toModel()
{
	_pModel = new oModel(this);
	_pModel->initWithTexture(_texture, CCSpriteBatchNode::defaultCapacity);
	oModelDef::visit(_root, _pModel);
	//Make model`s none look applied below.
	CCObject* child;
	CCARRAY_FOREACH(_pModel->getChildren(), child)
	{
		CCNode* node = (CCNode*)child;
		node->setIsVisible(false);
	}
	_pModel->autorelease();
	return _pModel;
}

const CCPoint& oModelDef::getKeyPoint( uint32 index ) const
{
	if (index >= _keys.size())
	{
		return CCPointZero;
	}
	return _keys[index];
}

string oModelDef::toXml()
{
	ostringstream stream;
	stream << '<' << char(oModelXml::Dorothy) << ' '
		<< char(oModelXml::File) << "=\"" << _clip << "\" ";
	if (_isFaceRight)
	{
		stream << char(oModelXml::FaceRight) << "=\"" << _clip << "\" ";
	}
	stream << "\">" << _root->toXml();
	BOOST_FOREACH(const oIndexMap::value_type& item, _actionIndex)
	{
		stream << '<' << char(oModelXml::ActionName) << ' '
			<< char(oModelXml::Index) << "=\"" << item.second << "\" "
			<< char(oModelXml::Name) << "=\"" << item.first.p << "\">";
	}
	BOOST_FOREACH(const oIndexMap::value_type& item, _lookIndex)
	{
		stream << '<' << char(oModelXml::LookName) << ' '
			<< char(oModelXml::Index) << "=\"" << item.second << "\" "
			<< char(oModelXml::Name) << "=\"" << item.first.p << "\">";
	}
	stream << "</" << char(oModelXml::Dorothy) << '>';
	return stream.str();
}

bool oModelDef::isFaceRight() const
{
	return _isFaceRight;
}

int oModelDef::getActionIndexByName( const string& name )
{
	auto it = _actionIndex.find(name);
	if (it != _actionIndex.end())
	{
		return it->second;
	}
	return -1;
}

int oModelDef::getLookIndexByName( const string& name )
{
	auto it = _lookIndex.find(name);
	if (it != _lookIndex.end())
	{
		return it->second;
	}
	return -1;
}

void oModelDef::setActionName( int index, const string& name )
{
	_actionIndex[name] = index;
}

void oModelDef::setLookName( int index, const string& name )
{
	_lookIndex[name] = index;
}

void oModelDef::addKeyPoint( const CCPoint& point )
{
	_keys.push_back(new CCPoint(point));
}